package remato.client.view.projecttree;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.PropertySheetPage;

import remato.client.controller.Registry;
import remato.client.controller.RequirementPlugin;
import remato.client.model.ClientModelItem;
import remato.client.model.facade.IMenuItemsProvider;
import remato.client.model.facade.IModelPersistable;
import remato.client.model.facade.ModelSystem;
import remato.client.view.editor.OpenModelEditorAction;
import remato.client.view.editor.properties.ExtendedPropertySheetSorter;
import remato.client.view.editor.properties.ExtendedPropertySourceProvider;
import remato.client.view.facade.IExplorerViewPart;
import remato.client.view.session.LoginAction;
import remato.client.view.session.LogoutAction;
import remato.client.view.util.ExtendedViewerSorter;
import remato.client.view.util.ModelByteArrayTransfer;
import remato.client.view.util.ModelItemDragSourceListener;
import remato.client.view.util.ModelItemLabelProvider;
import remato.client.view.util.ViewerAction;
import remato.client.view.util.contentproviders.ModelTreeViewContentProvider;

/**
 * @author petterei
 * @version $Id: ProjectTreeViewPart.java,v 1.3 2006-02-06 09:29:08 petterei Exp $
 */
public class ProjectTreeViewPart extends ViewPart implements IExplorerViewPart {

    private TreeViewer viewer;

    private DrillDownAdapter drillDownAdapter;

    private CreateProjectAction createRootProject;
    private LoginAction loginAction;
    private LogoutAction logoutAction;
    private SaveChangesAction saveChangesAction;
    private CreatePropertySetAction createPropertySetAction = null;
    private CreateStakeholderAction createStakeholderAction = null;
    private OpenModelEditorAction editor = null;
    
	/**
     * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
     */
    @Override
    public void init(IViewSite site) throws PartInitException {
        super.init(site);
        RequirementPlugin.addExplorerViewPart(this);
        RequirementPlugin.setCurrentExplorer(this);
        RequirementPlugin.addModelChangedListener(this);
        RequirementPlugin.addModelUpdateListener(this);
    }
    
    /**
	 * This is a callback that will allow us
	 * to create the viewer and initialize it.
	 */
	@Override
	public void createPartControl(Composite parent) {
		viewer = new TreeViewer(parent,
                SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL); // SWT.SINGLE could be replaced by SWT.MULTI, but then...
		drillDownAdapter = new DrillDownAdapter(viewer);
		viewer.setContentProvider(new ModelTreeViewContentProvider());
		viewer.setLabelProvider(new ModelItemLabelProvider());
		viewer.setInput(null);

        viewer.setSorter(new ExtendedViewerSorter());
        
        //drag
        int dragOperations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK;
        Transfer[] dragTransferTypes = new Transfer[] { ModelByteArrayTransfer.getInstance() };
        viewer.addDragSupport(dragOperations, dragTransferTypes, new ModelItemDragSourceListener(viewer));
        
        //drop
        int dropOperations = DND.DROP_MOVE;
        Transfer[] dropTransferTypes = new Transfer[] { ModelByteArrayTransfer.getInstance() };
        viewer.addDropSupport(dropOperations, dropTransferTypes, new ProjectTreePartDropAdapter(viewer));

        //Connects the view to the workbench.. (http://eclipse.org/articles/Article-Properties-View/properties-view.html)
        getSite().setSelectionProvider(viewer);

        makeActions();
		hookContextMenu();
		hookDoubleClickAction();
		contributeToActionBars();
		enableOrDisableActions();
	}

    @Override
    public void dispose() {
    	//TODO current explorer..
    	RequirementPlugin.removeModelChangedListener(this);
        RequirementPlugin.removeModelUpdateListener(this);
        super.dispose();
    }

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new MenuListenerImpl(this));
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, viewer);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		bars.getMenuManager().removeAll();
        bars.getToolBarManager().removeAll();
        fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
        bars.getToolBarManager().update(true);
	}

	private void fillLocalPullDown(IMenuManager manager) {
        if (RequirementPlugin.getInstance().isLoggedIn()) {
            manager.add(saveChangesAction);
            manager.add(new Separator());
            manager.add(createRootProject);
    		manager.add(new Separator());
            manager.add(logoutAction);
        } else {
            manager.add(loginAction);
        }
	}

	protected void fillContextMenu(IMenuManager manager) {
        if (RequirementPlugin.getInstance().isLoggedIn()) {

        	// item-related actions
        	IStructuredSelection selection = (IStructuredSelection)viewer.getSelection();
        	if (selection.size() == 1 ) {
	            Object object = selection.getFirstElement();
	            if ( object instanceof IMenuItemsProvider) {
	                for (ViewerAction action : ((IMenuItemsProvider)object).getMenuItemActions()) {
	                    action.setViewer(viewer);
	                    manager.add(action);
	                }
	                manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	            }
        	}


//            manager.add(createRootProject);
//    		manager.add(createStakeholderAction);
//    		manager.add(createPropertySetAction);
            manager.add(new Separator());
    		drillDownAdapter.addNavigationActions(manager);
    		// Other plug-ins can contribute there actions here
    		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
            manager.add(logoutAction);
        } else {
            manager.add(loginAction);
        }
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}
	
	private void fillLocalToolBar(IToolBarManager manager) {
        if (RequirementPlugin.getInstance().isLoggedIn()) {
            manager.add(saveChangesAction);
            manager.add(new Separator());
            manager.add(createRootProject);
    		manager.add(createStakeholderAction);
    		manager.add(createPropertySetAction);
    		manager.add(new Separator());
    		drillDownAdapter.addNavigationActions(manager);
            manager.add(new Separator());
            manager.add(logoutAction);
        } else {
            manager.add(loginAction);
        }
	}

	private void makeActions() {
        loginAction = new LoginAction(getSite().getWorkbenchWindow()) ;
        logoutAction = new LogoutAction() ;

        createPropertySetAction = new CreatePropertySetAction();
        createPropertySetAction.setViewer(viewer);
        createStakeholderAction = new CreateStakeholderAction();
        createStakeholderAction.setViewer(viewer);
        
        createRootProject = new CreateProjectAction(null);
        createRootProject.setText("New Root-ProjectWrapper");
        createRootProject.setToolTipText("Create a new Root ProjectWrapper");
        createRootProject.setDisplayMessage("New Root-ProjectWrapper created.", viewer);

        saveChangesAction = new SaveChangesAction();
        
        editor = new OpenModelEditorAction(null){
    		@Override
			public void run() {
    			ISelection selection = viewer.getSelection();
    			Object obj = ((IStructuredSelection)selection).getFirstElement();
    			if (obj  instanceof ClientModelItem) {
    				RequirementPlugin.getInstance().getEditorWindowManager().startEditorPart(Registry.MODEL_EDITOR, (ClientModelItem)obj);
    			}
    		}
        };
	}
    
	private void hookDoubleClickAction() {
		viewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(@SuppressWarnings("unused") DoubleClickEvent event) {
				editor.run();
			}
		});
	}
	
	/**
	 * Passing the focus request to the viewer's control.
	 */
	@Override
	public void setFocus() {
		viewer.getControl().setFocus();
		//(no need..)        viewer.setSelection(viewer.getSelection(), true);
	}

    /**
     * could use IPropertyListener for finer granularity..
     */
    public void onModelUpdate(IModelPersistable item) {
        getSite().setSelectionProvider(null);
        enableOrDisableActions();
        viewer.refresh(item, true);
//        viewer.refresh(true);
        getSite().setSelectionProvider(viewer);
    }

	/**
	 * @see remato.client.view.facade.IModelUpdateListener#onModelUpdate(IModelPersistable)
	 */
	public void onModelChange(@SuppressWarnings("unused") IModelPersistable targetChangeItem) { //TODO: update with finer granularity?
//    	viewer.getVisibleExpandedElements();
    	viewer.setSelection(null);
        enableOrDisableActions();
        viewer.refresh(true);
	}
    
    private void enableOrDisableActions() {
        saveChangesAction.setEnabled(isDirty());
    }

    /**
     * Reset should only be called after dirtychecking is complete.
	 * @see remato.client.view.facade.IExplorerViewPart#reset()
	 */
	public void reset() {
		viewer.setInput(null);
		contributeToActionBars();
	}

	/**
     * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
     */
    @Override
    public Object getAdapter(Class adapter) {
//        RequirementPlugin.log(getClass(), "Adapter asked for: " + adapter.getName());
        if (adapter == IPropertySheetPage.class) {
            return new PropertySheetPage(){
                
                @Override
                public void createControl(Composite parent) {
                    super.createControl(parent);
                    setSorter( new ExtendedPropertySheetSorter() );
                }
                /**
                 * @see org.eclipse.ui.views.properties.PropertySheetPage#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
                 */
                @Override
                public void selectionChanged(IWorkbenchPart part, ISelection selection) {
                    if (selection instanceof IStructuredSelection && ((IStructuredSelection)selection).getFirstElement() instanceof ClientModelItem ) {
                        setPropertySourceProvider(new ExtendedPropertySourceProvider());
                        
                    }
                                        
                    super.selectionChanged(part, selection);
                }
                
                
            };
        }
        return super.getAdapter(adapter);
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
     */
    public void doSave(IProgressMonitor monitor) {
		IStatus status = null;
		monitor.beginTask("Saving all modified items.", IProgressMonitor.UNKNOWN);
		showBusy(true);
		try {
			RequirementPlugin.getInstance().getSession().saveAll();
		} catch (Exception e) {
			status = new Status(IStatus.ERROR, Registry.PLUGIN_ID, 0, "Save did not work.", e );
		} finally {
			monitor.done();
			showBusy(false);
		}				
		if (null == status) {
			status = new Status(IStatus.INFO, Registry.PLUGIN_ID, 0, "Save was performed.", null );
		}
//		RequirementPlugin.log(status);
//        	Job j = new Job("Save all changes") {
//    		
//			/**
//			 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
//			 */
//			@Override
//			protected IStatus run(IProgressMonitor monitor) {
//				IStatus status = null;
//				monitor.beginTask("Saving all modified items.", IProgressMonitor.UNKNOWN);
//				showBusy(true);
//				try {
//					RequirementPlugin.getInstance().getSession().saveAllChangesInOneBatch();
//				} catch (Exception e) {
//					status = new Status(IStatus.ERROR, Registry.PLUGIN_ID, 0, "Save did not work.", e );
//				} finally {
//					monitor.done();
//					showBusy(false);
//				}				
//				if (null != status) {
//					status = new Status(IStatus.INFO, Registry.PLUGIN_ID, 0, "Save was performed.", null );
//				}
//				RequirementPlugin.log(status);
//				return status;
//			}
//    	};
//    	j.schedule();
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#doSaveAs()
     */
    public void doSaveAs() {
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#isDirty()
     */
    public boolean isDirty() {
        boolean dirty = false;
        Object o = viewer.getInput();
        if (o instanceof IModelPersistable || o instanceof ModelSystem[]) {
        	dirty = RequirementPlugin.getInstance().getSession().isDirty();
        }
        return dirty;
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
     */
    public boolean isSaveAsAllowed() {
        return false;
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded()
     */
    public boolean isSaveOnCloseNeeded() {
        return isDirty();
    }

    public void setContentInput(ModelSystem[] systems) {
        contributeToActionBars();
        viewer.setInput(systems);
        viewer.refresh();
    }

	public void onModelItemDeleted(IModelPersistable item) {
		viewer.remove(item);
        enableOrDisableActions();
	}

	public void onNewModelItemCreated(@SuppressWarnings("unused") IModelPersistable item) { // TODO: use this?
	}


}